﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GenericTools
{
    /// <summary>
    /// Class to select more than one item in the ListBox
    /// </summary>
    [System.Diagnostics.DebuggerNonUserCode()]
    public partial class ListBoxSelection : UserControl
    {
        #region Class Variables
        private List<ListItem> datasource;
        private List<ListItem> selectedList;
        private List<ListItem> availableList;
        #endregion

        #region Public Events
        public delegate void ItemMovedHandler(object sender, ListBoxSelectionEventArgs e);
        public event ItemMovedHandler ItemMoved;
        #endregion

        #region Constructors
        public ListBoxSelection()
        {
            InitializeComponent();
            
            this.selectedList = new List<ListItem>();
            this.availableList = new List<ListItem>();
            ItemMoved += new ItemMovedHandler(ListBoxSelection_ItemMoved);
            UpdateMovementButtons();
        }

        public ListBoxSelection(List<ListItem> datasource)
        {
            InitializeComponent();
            
            this.selectedList = new List<ListItem>();
            this.availableList = new List<ListItem>();
            ItemMoved += new ItemMovedHandler(ListBoxSelection_ItemMoved);
            
            if (datasource != null)
            {
                this.datasource = datasource;
                lstAvailable.Items.Clear();
                foreach (ListItem item in datasource) lstAvailable.Items.Add(item);
            }
            UpdateMovementButtons();
        }
        #endregion

        #region Private Helper Methods
        private void UpdateMovementButtons()
        {
            bool addStatus = (lstAvailable.Items.Count > 0);
            bool removeStatus = (lstSelected.Items.Count > 0);

            btnAdd.Enabled = addStatus;
            btnAddAll.Enabled = addStatus;

            btnRemove.Enabled = removeStatus;
            btnRemoveAll.Enabled = removeStatus;
        }

        /// <summary>
        /// Add an item from listbox source to listbox destination
        /// </summary>
        /// <param name="source">Source of the item</param>
        /// <param name="destination">Destination of the item</param>
        private void AddListItem(ListBox source, ListBox destination, ListBoxSelectionEventArgs.MovementType type)
        {
            ListItem selectedItem = (ListItem)source.SelectedItem;
            int selectedIndex = source.SelectedIndex;
            source.Items.RemoveAt(selectedIndex);

            if (source.Items.Count > 0)
            {
                if (selectedIndex != 0) selectedIndex--;
                source.SelectedIndex = selectedIndex;
            }

            destination.Items.Add(selectedItem);
            destination.SelectedIndex = destination.Items.Count - 1;

            ListBoxSelectionEventArgs e = new ListBoxSelectionEventArgs(selectedItem, source, destination, type);
            ItemMoved(this, e);
        }

        private void AddAllListItems(ListBox source, ListBox destination, ListBoxSelectionEventArgs.MovementType type)
        {
            source.SuspendLayout();
            destination.SuspendLayout();
            foreach (object item in source.Items)
            {
                ListItem listBoxItem = (ListItem)item;
                destination.Items.Add(listBoxItem);
                ListBoxSelectionEventArgs e = new ListBoxSelectionEventArgs(listBoxItem, source, destination, type);
                ItemMoved(this, e);
            }
            source.Items.Clear();
            destination.SelectedIndex = destination.Items.Count - 1;

            destination.ResumeLayout();
            source.ResumeLayout();
        }
        #endregion

        #region Event Handlers
        private void btnAdd_Click(object sender, EventArgs e)
        {
            if (lstAvailable.SelectedIndex == -1) lstAvailable.SelectedIndex = 0;
            ListItem selectedItem = (ListItem)lstAvailable.SelectedItem;
            ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.AVAILABLE_TO_SELECTED;
            AddListItem(lstAvailable, lstSelected, type);
            UpdateMovementButtons();
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            if (lstSelected.SelectedIndex == -1) lstSelected.SelectedIndex = 0;
            ListItem selectedItem = (ListItem)lstSelected.SelectedItem;
            ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.SELECTED_TO_AVAILABLE;
            AddListItem(lstSelected, lstAvailable, type);
            UpdateMovementButtons();
        }

        private void btnAddAll_Click(object sender, EventArgs e)
        {
            ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.AVAILABLE_TO_SELECTED;
            AddAllListItems(lstAvailable, lstSelected, type);
            UpdateMovementButtons();
        }

        private void btnRemoveAll_Click(object sender, EventArgs e)
        {
            ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.SELECTED_TO_AVAILABLE;
            AddAllListItems(lstSelected, lstAvailable, type);
            UpdateMovementButtons();
        }

        private void lstAvailable_DoubleClick(object sender, EventArgs e)
        {
            if (lstAvailable.Items.Count > 0)
            {
                if (lstAvailable.SelectedIndex == -1) lstAvailable.SelectedIndex = 0;
                ListItem selectedItem = (ListItem)lstAvailable.SelectedItem;
                ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.AVAILABLE_TO_SELECTED;
                AddListItem(lstAvailable, lstSelected, type);
                UpdateMovementButtons();
            }
        }

        private void lstSelected_DoubleClick(object sender, EventArgs e)
        {
            if (lstSelected.Items.Count > 0)
            {
                if (lstSelected.SelectedIndex == -1) lstSelected.SelectedIndex = 0;
                ListItem selectedItem = (ListItem)lstSelected.SelectedItem;
                ListBoxSelectionEventArgs.MovementType type = ListBoxSelectionEventArgs.MovementType.SELECTED_TO_AVAILABLE;
                AddListItem(lstSelected, lstAvailable, type);
                UpdateMovementButtons();
            }
        }

        private void ListBoxSelection_ItemMoved(object sender, ListBoxSelectionEventArgs e)
        {
            switch (e.Type)
            {
                case ListBoxSelectionEventArgs.MovementType.AVAILABLE_TO_SELECTED:
                    this.availableList.Remove(e.Item);
                    this.selectedList.Add(e.Item);
                    break;

                case ListBoxSelectionEventArgs.MovementType.SELECTED_TO_AVAILABLE:
                    this.selectedList.Remove(e.Item);
                    this.availableList.Add(e.Item);
                    break;
            }
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or sets the datasource
        /// </summary>
        [Browsable(false)]
        public List<ListItem> DataSource
        {
            get
            {
                return this.datasource;
            }
            set
            {
                this.lstSelected.Items.Clear();
                this.selectedList.Clear();
                this.lstAvailable.Items.Clear();
                this.availableList.Clear();
                if (value != null)
                {
                    this.datasource = value;
                    foreach (ListItem item in this.datasource)
                    {
                        this.lstAvailable.Items.Add(item);
                        this.availableList.Add(item);
                    }
                }
                UpdateMovementButtons();
            }
        }

        /// <summary>
        /// Gets or sets Title of the control
        /// </summary>
        [Description("Title of the control")]
        [Category("Appearance")]
        public string Title
        {
            get
            {
                return this.gboSelection.Text;
            }
            set
            {
                this.gboSelection.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets title of available list
        /// </summary>
        [Description("Title of available list")]
        [Category("Appearance")]
        public string AvailableTitle
        {
            get
            {
                return this.lblAvailable.Text;
            }
            set
            {
                lblAvailable.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets title of selected list
        /// </summary>
        [Description("Title of selected list")]
        [Category("Appearance")]
        public string SelectedTitle
        {
            get
            {
                return this.lblSelected.Text;
            }
            set
            {
                lblSelected.Text = value;
            }
        }

        /// <summary>
        /// Gets label object of available list
        /// </summary>
        [Browsable(false)]
        public Label AvailableLabel
        {
            get
            {
                return this.lblAvailable;
            }
        }

        /// <summary>
        /// Gets label object of selected list
        /// </summary>
        [Browsable(false)]
        public Label SelectedLabel
        {
            get
            {
                return this.lblSelected;
            }
        }

        /// <summary>
        /// Gets selected list
        /// </summary>
        [Browsable(false)]
        public List<ListItem> SelectedList
        {
            get
            {
                return this.selectedList;
            }
        }

        /// <summary>
        /// Gets available list
        /// </summary>
        [Browsable(false)]
        public List<ListItem> AvailableList
        {
            get
            {
                return this.availableList;
            }
        }
        #endregion
    }
}
